#include <stdio.h>
#include "pretty.h"
int tabno = 0;

void prettyTAB() {
	int i;
	for (i = 0; i < tabno; i++) {
		printf("   ");
	}
}
void prettyBODY(BODY *b, SymbolTable *rootTable) {
	prettyDECL_LIST(b->decl_list, rootTable);
	prettySTATEMENT_LIST(b->statement_list, rootTable);
}

void prettyEXPRESSION(EXPRESSION *e, SymbolTable *symboltable) {
	switch (e->kind) {
	case timesK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_times.left, symboltable);
		printf("*");
		prettyEXPRESSION(e->val.EXP_times.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case divK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_div.left, symboltable);
		printf("/");
		prettyEXPRESSION(e->val.EXP_div.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case plusK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_plus.left, symboltable);
		printf("+");
		prettyEXPRESSION(e->val.EXP_plus.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case minusK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_minus.left, symboltable);
		printf("-");
		prettyEXPRESSION(e->val.EXP_minus.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case orK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_or.left, symboltable);
		printf(" || ");
		prettyEXPRESSION(e->val.EXP_or.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case andK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_and.left, symboltable);
		printf(" && ");
		prettyEXPRESSION(e->val.EXP_and.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case leqK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_leq.left, symboltable);
		printf(" <= ");
		prettyEXPRESSION(e->val.EXP_leq.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case geqK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_geq.left, symboltable);
		printf(" >= ");
		prettyEXPRESSION(e->val.EXP_geq.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case leK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_lessthan.left, symboltable);
		printf("<");
		prettyEXPRESSION(e->val.EXP_lessthan.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case grK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_greaterthan.left, symboltable);
		printf(">");
		prettyEXPRESSION(e->val.EXP_greaterthan.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case eqK:
		printf("(");
		prettyEXPRESSION(e->val.EXP_eq.left, symboltable);
		printf(" == ");
		prettyEXPRESSION(e->val.EXP_eq.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case neqK:
		printf("(");
		prettyEXPRESSION(e->val.EXPneq.left, symboltable);
		printf(" != ");
		prettyEXPRESSION(e->val.EXPneq.right, symboltable);
		printf(")");
		prettyEXP_TYPE(e->type, symboltable);
		break;
	case termK:
		prettyTERM(e->val.term, symboltable);
		break;
	}
}

void prettyFUNCTION(FUNCTION *func, SymbolTable *symboltable) {
	prettyHEAD(func->head, symboltable);
	tabno++;
	prettyBODY(func->body, func->table);
	tabno--;
	prettyTAIL(func->tail);
}

void prettyHEAD(HEAD *head, SymbolTable *symboltable) {
	prettyTAB();
	printf("func %s(", head->id);
	prettyPAR_DECL_LIST(head->par_decl_list, symboltable);
	printf("): ");
	prettyTYPE(head->type, symboltable);
	printf("\n");
}

void prettyTAIL(TAIL *tail) {
	prettyTAB();
	printf("end %s \n", tail->id);
}

void prettyTYPE(TYPE *type, SymbolTable *symboltable) {
	switch (type->kind) {
	case type_idK:
		printf("%s", type->val.id);
		break;
	case intK:
		printf("int");
		break;
	case boolK:
		printf("bool");
		break;
	case arrayK:
		printf("array of ");
		prettyTYPE(type->val.type, symboltable);
		break;
	case recordK:
		printf("record of {");
		prettyVAR_DECL_LIST(type->val.recordE.var_decl_list, symboltable);
		printf("}");
		break;
	}

}

void prettyPAR_DECL_LIST(PAR_DECL_LIST * par_decl_list,
		SymbolTable *symboltable) {
	if (!par_decl_list == 0) {
		prettyVAR_DECL_LIST(par_decl_list->var_decl_list, symboltable);
	}
}

void prettyVAR_DECL_LIST(VAR_DECL_LIST *var_decl_list, SymbolTable *symboltable) {
	switch (var_decl_list->kind) {
	case var_decl_listK:
		prettyVAR_DECL_LIST(var_decl_list->var_decl_list, symboltable);
		printf(", ");
		prettyVAR_TYPE(var_decl_list->var_type, symboltable);
		break;
	case var_decl_list_baseK:
		prettyVAR_TYPE(var_decl_list->var_type, symboltable);
		break;
	}
}

void prettyVAR_TYPE(VAR_TYPE *var_type, SymbolTable *symboltable) {
	printf("%s:", var_type->id);
	prettyTYPE(var_type->type, symboltable);
}

void prettyDECL_LIST(DECL_LIST *decl_list, SymbolTable *symboltable) {
	if (!decl_list == 0) {
		prettyDECL_LIST(decl_list->decl_list, symboltable);
		prettyDECLARATION(decl_list->declaration, symboltable);
	}
}

void prettyDECLARATION(DECLARATION *declaration, SymbolTable *symboltable) {
	switch (declaration->kind) {
	case typeidK:
		prettyTAB();
		printf("type ");
		printf("%s ", declaration->val.DECL_TYPE.id);
		printf("= ");
		prettyTYPE(declaration->val.DECL_TYPE.type, symboltable);
		printf(";\n");
		break;
	case funcK:
		prettyFUNCTION(declaration->val.function, symboltable);
		break;
	case varK:
		prettyTAB();
		printf("var ");
		prettyVAR_DECL_LIST(declaration->val.var_decl_list, symboltable);
		printf(";\n");
		break;
	}
}

void prettySTATEMENT_LIST(STATEMENT_LIST *statement_list,
		SymbolTable *symboltable) {
	switch (statement_list->kind) {
	case statement_list_baseK:
		prettySTATEMENT(statement_list->statement, symboltable);
		break;
	case statement_listK:
		prettySTATEMENT_LIST(statement_list->statement_list, symboltable);
		if(statement_list->statement!=NULL){
			prettySTATEMENT(statement_list->statement, symboltable);
		}
		break;
	}
}
void prettySTATEMENT(STATEMENT *statement, SymbolTable *symboltable) {
	prettyTAB();
	switch (statement->kind) {
	case returnK:
		printf("return ");
		prettyEXPRESSION(statement->val.return_expression, symboltable);
		printf(";\n");
		break;
	case writeK:
		printf("write");
		printf("(");
		prettyEXPRESSION(statement->val.write_expression, symboltable);
		printf(")");
		printf(";\n");
		break;
	case allocateK:
		printf("allocate ");
		prettyVARIABLE(statement->val.allocateE.variable, symboltable);
		prettyOPT_LENGTH(statement->val.allocateE.opt_length, symboltable);
		printf(";\n");
		break;
	case variableK:
		prettyVARIABLE(statement->val.variableE.variable, symboltable);
		printf(" = ");
		prettyEXPRESSION(statement->val.variableE.expression, symboltable);
		printf(";\n");
		break;
	case ifK:
		printf("if");
		prettyEXPRESSION(statement->val.ifE.expression, symboltable);
		printf(" then\n");
		tabno++;
		prettySTATEMENT(statement->val.ifE.statement, symboltable);
		tabno--;
		prettyOPT_ELSE(statement->val.ifE.opt_else, symboltable);
		break;
	case whileK:
		printf("while");
		prettyEXPRESSION(statement->val.whileE.expression, symboltable);
		printf(" do\n");
		tabno++;
		prettySTATEMENT(statement->val.whileE.statement, symboltable);
		tabno--;
		break;
	case statementlistK:
		printf("{\n");
		tabno++;
		prettySTATEMENT_LIST(statement->val.statement_list, symboltable);
		tabno--;
		prettyTAB();
		printf("}\n");
		break;
	}
}

void prettyOPT_LENGTH(OPT_LENGTH *opt_length, SymbolTable *symboltable) {
	if (!opt_length == 0) {
		printf(" of length ");
		prettyEXPRESSION(opt_length->expression, symboltable);
	}
}

void prettyOPT_ELSE(OPT_ELSE *opt_else, SymbolTable *symboltable) {
	if (!opt_else == 0) {
		prettyTAB();
		printf("else\n");
		tabno++;
		prettySTATEMENT(opt_else->statement, symboltable);
		tabno--;
	}
}

void prettyVARIABLE(VARIABLE *variable, SymbolTable *symboltable) {
	TYPE *type;
	switch (variable->kind) {
	case var_idK:
		type = variable->type;
		printf("%s", variable->val.id);
		prettyEXP_TYPE(type, symboltable);
		break;
	case var_expK:
		prettyVARIABLE(variable->val.var_expE.variable, symboltable);
		printf("[");
		prettyEXPRESSION(variable->val.var_expE.expression, symboltable);
		printf("]");
		prettyEXP_TYPE(variable->type, symboltable);
		break;
	case var_dotK:
		prettyVARIABLE(variable->val.var_dotE.variable, symboltable);
		printf(".");
		printf("%s", variable->val.var_dotE.id);
		prettyEXP_TYPE(variable->type, symboltable);
		break;
	}
}

void prettyTERM(TERM *term, SymbolTable *symboltable) {
	switch (term->kind) {
	case term_varK:
		prettyVARIABLE(term->val.variable, symboltable);
		break;
	case term_idK:
		printf("%s", term->val.term_idE.id);
		printf("(");
		prettyACT_LIST(term->val.term_idE.act_list, symboltable);
		printf(")");
		prettyEXP_TYPE(term->type, symboltable);
		break;
	case term_expK:
		prettyEXPRESSION(term->val.expression, symboltable);
		break;
	case term_notK:
		printf("(!");
		prettyTERM(term->val.term, symboltable);
		printf(")");
		break;
	case term_act_expK:
		printf("|");
		prettyEXPRESSION(term->val.act_expression, symboltable);
		printf("|");
		break;
	case term_numK:
		printf("%d", term->val.number);
		prettyEXP_TYPE(term->type, symboltable);
		break;
	case term_trueK:
		printf("%s", term->val.bn);
		prettyEXP_TYPE(term->type, symboltable);
		break;
	case term_falseK:
		printf("%s", term->val.bn);
		prettyEXP_TYPE(term->type, symboltable);
		break;
	case term_nullK:
		printf("%s", term->val.bn);
		prettyEXP_TYPE(term->type, symboltable);
		break;
	}
}

void prettyEXP_TYPE(TYPE *type, SymbolTable *symboltable) {
	SYMBOL *symbol;
	VAR_TYPE *var_type;
	if (type == NULL) {
		printf("(*NULL*)");
	} else {
		switch (type->kind) {
		case type_idK:
			symbol = getSymbol(symboltable, type->val.id);
			if (symbol != NULL) {
				var_type = symbol->val.var_type;
				printf("(*%s*)",symbol->name);
				//prettyEXP_TYPE(var_type->type, symboltable);
			}
			break;
		case intK:
			printf("(*int*)");
			break;
		case boolK:
			printf("(*bool*)");
			break;
		case arrayK:
			printf("(*array*)");
			prettyEXP_TYPE(type->val.type, symboltable);
			break;
		case recordK:
			printf("(*record*)");
			//prettyVAR_DECL_LIST(type->val.recordE.var_decl_list);
			break;
		}
	}
}
void prettyACT_LIST(ACT_LIST *act_list, SymbolTable *symboltable) {
	if (!act_list == 0) {
		prettyEXP_LIST(act_list->exp_list, symboltable);
	}
}

void prettyEXP_LIST(EXP_LIST *exp_list, SymbolTable *symboltable) {

	switch (exp_list->kind) {
	case exp_list_baseK:
		prettyEXPRESSION(exp_list->expression, symboltable);
		break;
	case exp_listK:
		prettyEXP_LIST(exp_list->exp_list, symboltable);
		printf(", ");
		prettyEXPRESSION(exp_list->expression, symboltable);
		break;
	}
}
